package Server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import Client.ClientListener;
import Client.CloseEvent;
import Client.MsgEvent;
import DBUtil.CNN;
import Tools.Log;
import XML.XML;
import XML.XMLNode;

/**
 * 多线程网关服务端
 * 
 * 离线消息储备未完成。
 * 
 * @author Administrator
 * 
 */
public class GatewayServerManager extends Server implements ServerListener, ClientListener {
	public static int port = 1356;
	private volatile static GatewayServerManager instance;// 单例
	
	private volatile Thread broadThread;
	private volatile Queue<Message> messages = new ConcurrentLinkedQueue<Message>(); // 广播消息池

	private String userid;
	private long gateTimeout;
	private Client.ClientThread client;
	private long lasttime;

	public class GatewayClientThread extends ClientThread{
		private BufferedInputStream in;
		private BufferedOutputStream out;
		
		public GatewayClientThread(Socket client, Server s) throws IOException {
			super(client, s);
		}
		
		@Override
		protected void initStream() throws IOException {
			in = new BufferedInputStream(client.getInputStream());
			out = new BufferedOutputStream(client.getOutputStream());
		}
		
		@Override
		protected void runReadNetBytes() throws IOException {
			int size = 0;
			byte [] buf = new byte[1024];
			while((size=in.read(buf))>0){
				GatewayServerManager.this.client.send(buf,0,size);
			}
		}
		
		@Override
		public void send(byte[] buf, int offset, int len) {
			try {
				out.write(buf, offset, len);
				out.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	/**
	 * 启动/获取服务端
	 * 
	 * @return
	 * @throws IOException
	 */
	public static GatewayServerManager getServer() {
		if (instance == null) {
			try {
				port = Integer.parseInt(CNN.getConfig().GetPathValue("Serv:port").toString());
				instance = new GatewayServerManager(port);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return instance;
	}
	
	private GatewayServerManager(final int port) throws IOException {
		super(port);

		String address = CNN.getConfig().GetPathValue("Serv:address").toString();
		int sport = Integer.parseInt(CNN.getConfig().GetPathValue("Serv:sport").toString());
		gateTimeout = Integer.parseInt(CNN.getConfig().GetPathValue("Serv:gate-timeout").toString());
		// ///////////////////初始化////////////////////////
		client = new Client.ClientThread(address, sport) {
			BufferedInputStream in;
			BufferedOutputStream out;
			protected void ConnectAfterInitStream() throws IOException {
				in = new BufferedInputStream(s.getInputStream());
				out = new BufferedOutputStream(s.getOutputStream());
			}
			protected void runReadNetBytes() {
				int size = 0;
				byte[] buf = new byte[2*1024*1024];
				try {
					while((size=in.read(buf))>0){
						lasttime = System.currentTimeMillis();
						for (Entry<Long, ClientThread> e : clientTidCache.entrySet()) {
							e.getValue().send(buf,0,size);
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
				}finally{
					try {
						s.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
					thread = null;
				}
			}
			public synchronized void send(byte[] data,int offset,int len) {
				try {
					lasttime = System.currentTimeMillis();
					out.write(data, offset, len);
					out.flush();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		};
		client.addClientListenser(this);
		// ///////////////////初始化广播线程////////////////////

		broadThread = new Thread() {
			@Override
			public void run() {
				log("Broadcast thread startting...");
				while (broadThread == Thread.currentThread()) {
					try {
						Thread.sleep(100);
						//Log.print("Check gate timeout: "+System.currentTimeMillis()+"-"+lasttime+"="+(System.currentTimeMillis()-lasttime)+"/"+gateTimeout);
						if(lasttime>0&&System.currentTimeMillis()-lasttime>gateTimeout){
							client.Close();
						}
						if (client.isConnected() == false) {
							String address = CNN.getConfig().GetPathValue("Serv:address").toString();
							int sport = Integer.parseInt(CNN.getConfig().GetPathValue("Serv:sport").toString());
							client.setAddress(address, sport);
							if (!client.Connect()) {
								continue;
							}else{
								lasttime = System.currentTimeMillis();
							}
						}
						while (!messages.isEmpty()) {
							Message msg = messages.poll();
							if (msg.type == Message.COMMON) {

							} else
								// //////////////发送内/外网广播//////////////////////
								for (Long tid : clientTidCache.keySet()) {
									ClientThread t = clientTidCache.get(tid);
									if (t.getLogincmd() == null) {
										continue;
									}
									boolean b = msg.type == Message.ALL ? true : t.isOuterNet() ? msg.type == Message.OUTERNET : msg.type == Message.LAN;
									if (b && t != null) {
										String code = msg.msg.get("code");
										if (code != null && Long.parseLong(code) == t.getId()) {
											continue;
										}
										t.sendMessage(msg.msg);
									}
								}
							// ///////////////////////////////////////////
						}
						// 检测心跳超时
						long currTime = System.currentTimeMillis();
						int liveTimeout = Integer.parseInt(CNN.getConfig().GetPathValue("Serv:gate-timeout").toString());
						for (Entry<Long, ClientThread> ele : clientTidCache.entrySet()) {
							if(ele.getValue().lasttime>0&&currTime-ele.getValue().lasttime>liveTimeout){
								ele.getValue().close();
							}
						}
					} catch (Exception e) {
						log("[BroadThread]Excption:" + e);
					}
				}
				log("Broadcast thread end...");
			}
		};
		log("Server bind port: " + port);

		broadThread.start();// 启动广播线程
	}

	
	@Override
	protected ClientThread newClientThread(Socket client, Server server)
			throws IOException {
		return new GatewayClientThread(client, server);
	}
	
	@Override
	public void accepClientEvent(ClientAcceptEvent ace) {
		super.accepClientEvent(ace);// 进入客户池
		log(ace + "  Online: " + clientTidCache.size());

	}

	private boolean CheckPort(InetAddress address, int port) {
		Socket s = null;
		try {
			if (address.getHostAddress().equals("127.0.0.1"))
				return false;
			s = new Socket();
			s.connect(new InetSocketAddress(address, port), 3000);
			Log.print("[Check] client port: " + address + ":" + port + "  success");
			s.close();
			return true;
		} catch (IOException e) {
			Log.print("[Check] client port: " + address + ":" + port + "  fatal");
		} finally {
			if (s != null && !s.isClosed()) {
				try {
					s.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return false;
	}

	@Override
	public void ClientCloseEvent(ClientExitEvent cce) {
		super.ClientCloseEvent(cce);// 离开客户池
		log(cce);
		String username = cce.ct.getName();
		if (username != null && (!username.equals("*") || !username.equals(""))) {
			client.sendMessage("<Outline userid=\"" + cce.getCt().getName() + "\" />");// 通知好友下线
		}
	}

	/**
	 * 日志函数
	 * 
	 * @param log
	 * 
	 */
	private void log(Object log) {
		Log.print(log);

	}

	@Override
	public void onCloseEvent(CloseEvent cce) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onMsgEvent(MsgEvent ce) {
		XML cmd = ce.getCmd();
		String msgtype = cmd.get("msg_type");
		if (msgtype == null) {
			String gucode = cmd.get("gucode");
			// 转发到客户端
			if (gucode != null) {
				long code = Long.parseLong(gucode);
				ClientThread ct = getClientThread(code);
				if (ct == null) {
					String userid = cmd.get("userid");
					if(userid!=null){
						Log.print("Use  userid  ClientThread "+cmd);
						ct = getClientThread(userid);
						if (ct != null) {
							code = ct.getId();
						}
					}
				}else{
					if(cmd.FLAG.equals("OK")){// 登陆成功修改线程名
						Log.print("Change Thread.Name to UserId"+cmd);
						clientUidCache.remove(ct.getName());
						ct.setName(cmd.get("userid"));
						clientUidCache.put(ct.getName(), ct);
					}
				}
				sendMessage(code, cmd);
			}else {
				// 处理服务器端的消息
				Log.print("Handle Serv-MSG: "+cmd.FLAG+"  "+cmd);
				if (cmd.FLAG.equals("GOK")) {
					userid = cmd.get("userid");
					log("Gate-Login Success!");
				} else if (cmd.FLAG.equals("GNO")) {
					log("Gate-Login Fault!");
				} else if (cmd.FLAG.equals("Pulse")) {
					lasttime = System.currentTimeMillis();
					Log.print("Pulse: "+lasttime);
				} else if(cmd.FLAG.equals("Outline")){
					String userid = cmd.get("userid");
					ClientThread ct = getClientThread(userid);
					ct.sendMessage(cmd);
					for(;ct.getMessageCount()>0;){
						try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					ct.close();
				}
			}
		} else {
			pushMessage(new Message(cmd, msgtype != null ? Integer.parseInt(msgtype) : (Message.ALL)));
		}
	}

	/**
	 * 添加广播消息
	 * 
	 * @param msg
	 */
	public void pushMessage(Message msg) {
		// Log.print("[pushMessage]"+msg);
		messages.add(msg);
	}

	/***************************************************************************
	 * 【请求事件】客户端请求处理
	 **************************************************************************/
	@Override
	public void requestEvent(ClientRequestEvent se) {
		XML cmd = se.cmd;
		XML logincmd = se.getCt().getLogincmd(); // 取登陆验证有效指令

		if (logincmd == null) {
			validateCmdAct(se.getCt(), cmd); // 处理未验证用户请求
			return;
		}

		// ///////////////已验证用户请求处理//////////////////

		String flag = cmd.FLAG;
		String userId = se.getCt().getName();

		cmd.put("gucode", se.getCt().getId());

		if (flag.equalsIgnoreCase("seach")) {
			// //////////////////////////////////////////////// --> 搜索文件请求
			Log.print("搜索文件请求：" + cmd);
			cmd.put("userid", userId);

			client.sendMessage(cmd);

		} else if (flag.equalsIgnoreCase("sresult")) {
			// //////////////////////////////////////////////// --> 转发搜索结果
			cmd.put("userid", userId); // 将用户名替换成文件所有者用户名
			cmd.put("address", se.getCt().getSocket().getInetAddress().toString());// 用户IP地址
			client.sendMessage(cmd); // 转发给搜索者
			
		} else if (flag.equalsIgnoreCase("get")) {
			// //////////////////////////////////////////////// --> 索取文件种子请求
			Log.print("查找资源种子：" + cmd);
			// cmd.put("code", String.valueOf(se.getCt().getId()));
			client.sendMessage(cmd);// 通知全网用户
		} else if (flag.equalsIgnoreCase("pip")) {
			// //////////////////////////////////////////////// --> // 返回种子索取信息
			Log.print("返回文件种子：" + cmd);
			// long code = Long.parseLong(cmd.take("code"));
			if (se.getCt().isOuterNet()) {
				cmd.put("address", se.getCt().getSocket().getInetAddress().toString());
			}
			cmd.put("userid", userId); // 替换成文件列表所有者用户名
			client.sendMessage(cmd);
		} else if (flag.equalsIgnoreCase("dloadGet")) {
			Log.print("请求下载资源  >> " + cmd);

			client.sendMessage(cmd);

		} else if (flag.equalsIgnoreCase("dloadData")) {
			Log.print("[Forward] Pip:" + userId + "    Data: " + cmd.getData().length());
//			long code = Long.parseLong(cmd.get("code"));
//			cmd.put("tcode", code);
			client.sendMessage(cmd);
		} else if (flag.equalsIgnoreCase("msg")) {
			// //////////////////////////////////////////////// --> 发送用户消息
			cmd.put("s_userid", userId);
			client.sendMessage(cmd);

		} else if (flag.equalsIgnoreCase("GroupMsg")) {
			// //////////////////////////////////////////////// --> 发送群消息请求
			// int id = Integer.parseInt(cmd.get("id"));
			client.sendMessage(cmd);
		} else if (flag.equalsIgnoreCase("add")) {
			// //////////////////////////////////////////////// --> 好友添加请求
			// String id = cmd.get("userid");
			cmd.put("s_userid", userId);
			client.sendMessage(cmd);

		} else if (flag.equalsIgnoreCase("zhuce")) {
			// //////////////////////////////////////////////// --> 注册
			String zhuceCmd = cmd.get("cmd");
			if (zhuceCmd.equals("shen")) { // 获取省份
				client.sendMessage(cmd);
			} else if (zhuceCmd.equals("submit")) { // 注册提交
				Log.print("注册用户：" + cmd);
				client.sendMessage(cmd);
			}
		} else if (flag.equalsIgnoreCase("ResContribute")) {
			// String userid = cmd.get("userid");
			client.sendMessage(cmd);
		} else if (flag.equalsIgnoreCase("zhmm")) {
//			String userid = cmd.get("userid");
//			String tiwen = cmd.get("tiwen");
//			String daan = cmd.get("daan");
//			String pwd = cmd.get("pwd");

			client.sendMessage(cmd);
		} else if (flag.equalsIgnoreCase("update")) {
			if (cmd.get("cmd").equals("get")) {
				// String userid = cmd.get("userid");

				client.sendMessage(cmd);
			} else if (cmd.get("cmd").equals("pwd")) {
				// String userid = cmd.get("userid");
				client.sendMessage(cmd);
			} else if (cmd.get("cmd").equals("info")) {
				Log.print("修改资料：" + cmd);
				// String userid = cmd.get("userid");
				client.sendMessage(cmd);
			}

		} else if (flag.equalsIgnoreCase("talkmsg")) {
			Log.print("TalkMsg " + cmd);

			// String userid = cmd.get("userid");
			cmd.put("suserid", userId);
			client.sendMessage(cmd);

		} else if (flag.equalsIgnoreCase("talk")) {
			Log.print("Talk " + cmd);

			client.sendMessage(cmd);
			// 告诉对方弹出消息框

		} else if (flag.equalsIgnoreCase("resInfo")) {
			XMLNode xn = cmd;

			client.sendMessage(xn);
		} else if (flag.equalsIgnoreCase("pulse")) {
			se.getCt().lasttime = System.currentTimeMillis();
		} else {
			// ////////////////////////////////////////////////
			client.sendMessage(cmd);
//			Log.print("[未知指令]" + se.getCt().getSocket().getInetAddress()
//					+ " cmd:" + cmd);

		}
	}

	/**
	 * 处理未验证用户的请求，如：登陆请求
	 * 
	 */
	public boolean validateCmdAct(ClientThread ct, XML cmd) {
		String command = cmd.FLAG.toUpperCase();
		if (command.equalsIgnoreCase("login")) {
			String userid = cmd.get("userid");

			if (userid.equals("*")) {
				userid = ct.getName().replace("Thread", "User");
				clientUidCache.remove(ct.getName());
				clientUidCache.put(userid, ct);
				ct.setName(userid);
			}
			Log.print("Login User, userid=" + userid); // 登录成功！
			// =============保存用户的基本信息=================

			ct.setLogincmd(cmd.clone()); // 记录登录请求

			// 判断是否为外网
			String inet = cmd.get("inet");
			ct.setOuterNet(inet == null ? false : inet.equals(ClientThread.OUTER_NET) && CheckPort(ct.getSocket().getInetAddress(), cmd.getInt("sport")));
			cmd.put("gucode", ct.getId());
			cmd.put("address", ct.getSocket().getInetAddress().toString().substring(1));
			client.sendMessage(cmd);
			return true;
		}
		return false;
	}
}
